Run a simple experiment with Bluesky

This guide will walk you through the process of running a simple experiment using Blop, Bluesky, and Tiled. See https://blueskyproject.io/ for more information on the Bluesky ecosystem and how these tools work together.

Bluesky along with a data access backend (either Tiled or Databroker) are not necessary for using Blop, but are fully integrated into the package.

We’ll start by importing the necessary libraries.

import logging
import time
from typing import Any

from blop import DOF, Objective
from blop.ax import Agent
from blop.dofs import DOF
from blop.objectives import Objective

from bluesky.protocols import NamedMovable, Readable, Status, Hints, HasHints, HasParent
from bluesky.run_engine import RunEngine
from bluesky.callbacks.tiled_writer import TiledWriter
from bluesky.callbacks.best_effort import BestEffortCallback
from tiled.client import from_uri
from tiled.server import SimpleTiledServer

# Suppress noisy logs from httpx 
logging.getLogger("httpx").setLevel(logging.WARNING)
[WARNING 10-13 20:12:31] ax.service.utils.with_db_settings_base: Ax currently requires a sqlalchemy version below 2.0. This will be addressed in a future release. Disabling SQL storage in Ax for now, if you would like to use SQL storage please install Ax with mysql extras via `pip install ax-platform[mysql]`.

First, we will start up a Tiled server. This will act as our data access service which Bluesky will write to and that Blop can read from.

tiled_server = SimpleTiledServer()
2025-10-13 20:12:34.968 INFO: Subprocess stdout:
2025-10-13 20:12:34.969 INFO: Subprocess stderr: Database sqlite+aiosqlite:////tmp/tmpacs6ykdb/catalog.db is new. Creating tables.
Database initialized.
Tiled version 0.1.6
2025-10-13 20:12:35.284 INFO: Tiled version 0.1.6
2025-10-13 20:12:35.290 INFO: Context impl SQLiteImpl.
2025-10-13 20:12:35.291 INFO: Will assume non-transactional DDL.

Next, we’ll set up the Bluesky run engine and connect to the local Tiled server.

RE = RunEngine({})
tiled_client = from_uri(tiled_server.uri)
tiled_writer = TiledWriter(tiled_client)
RE.subscribe(tiled_writer)
bec = BestEffortCallback()
bec.disable_plots()
RE.subscribe(bec)
1

In order to control parameters and acquire data with Bluesky, we must follow the NamedMovable and Readable protocols. To do this, we implement a simple class that implements both protocols. An alternative to implementing these protocols yourself is to use Ophyd. The additional AlwaysSuccessfulStatus is necessary to tell the Bluesky RunEngine when a move is complete. For the purposes of this tutorial, every move is successful and complete immediately.

class AlwaysSuccessfulStatus(Status):
    def add_callback(self, callback) -> None:
        callback(self)

    def exception(self, timeout = 0.0):
        return None
    
    @property
    def done(self) -> bool:
        return True
    
    @property
    def success(self) -> bool:
        return True

class ReadableSignal(Readable, HasHints, HasParent):
    def __init__(self, name: str) -> None:
        self._name = name
        self._value = 0.0

    @property
    def name(self) -> str:
        return self._name

    @property
    def hints(self) -> Hints:
        return { 
            "fields": [self._name],
            "dimensions": [],
            "gridding": "rectilinear",
        }
    
    @property
    def parent(self) -> Any | None:
        return None

    def read(self):
        return {
            self._name: { "value": self._value, "timestamp": time.time() }
        }

    def describe(self):
        return {
            self._name: { "source": self._name, "dtype": "number", "shape": [] }
        }

class MovableSignal(ReadableSignal, NamedMovable):
    def __init__(self, name: str, initial_value: float = 0.0) -> None:
        super().__init__(name)
        self._value: float = initial_value

    def set(self, value: float) -> Status:
        self._value = value
        return AlwaysSuccessfulStatus()

Next, we’ll define the DOFs and optimization objective. Since we can calculate our objective based on the two movable signals, there is no need to acquire data using an extra readable. With the movables already configured via the DOFs, it is implicitly added as a readable during the data acquisition.

x1 = MovableSignal("x1", initial_value=0.1)
x2 = MovableSignal("x2", initial_value=0.23)

dofs = [
    DOF(movable=x1, search_domain=(-5, 5)),
    DOF(movable=x2, search_domain=(-5, 5)),
]
objectives = [
    Objective(name="himmelblau_2d", target="min"),
]
readables = []

Note

Additional readables are typically added as a list of devices that produce data, such as detectors, to help with computing the desired outcome via the digestion function.

Next, we will define the digestion function. The data that will be available to the digestion function will always be a collection of readables (specified either implicitly or explicitly).

def himmelblau_2d_digestion(trial_index: int, data: dict[str, Any]) -> float:
    x1 = data["x1"][trial_index % len(data["x1"])]
    x2 = data["x2"][trial_index % len(data["x2"])]
    return {"himmelblau_2d": (x1 ** 2 + x2 - 11) ** 2 + (x1 + x2 ** 2 - 7) ** 2}

Next, we will setup the agent and perform the optimization using the run engine.

agent = Agent(readables=readables, dofs=dofs, objectives=objectives, db=tiled_client, digestion=himmelblau_2d_digestion)
agent.configure_experiment(name="simple_experiment", description="A simple experiment.")
RE(agent.learn(iterations=30))
Transient Scan ID: 1     Time: 2025-10-13 20:12:35
Persistent Unique Scan ID: '9a845dee-b863-49b0-b632-fe52603de8a3'
New stream: 'primary'
+-----------+------------+------------+------------+
|   seq_num |       time |         x1 |         x2 |
+-----------+------------+------------+------------+
|         1 | 20:12:35.6 |      0.000 |      0.000 |
Found duckdb shared library at /home/runner/work/blop/blop/.pixi/envs/docs/lib/python3.12/site-packages/_duckdb.cpython-312-x86_64-linux-gnu.so
+-----------+------------+------------+------------+
generator list_scan ['9a845dee'] (scan num: 1)


Transient Scan ID: 2     Time: 2025-10-13 20:12:36
Persistent Unique Scan ID: '08435476-b66f-49c7-bead-4a23a3b24ab4'
New stream: 'primary'
+-----------+------------+------------+------------+
|   seq_num |       time |         x1 |         x2 |
+-----------+------------+------------+------------+
|         1 | 20:12:36.2 |      2.041 |      3.284 |
+-----------+------------+------------+------------+
generator list_scan ['08435476'] (scan num: 2)


Transient Scan ID: 3     Time: 2025-10-13 20:12:36
Persistent Unique Scan ID: 'f03313c9-7c35-497d-84e3-838cb744b10c'
New stream: 'primary'
+-----------+------------+------------+------------+
|   seq_num |       time |         x1 |         x2 |
+-----------+------------+------------+------------+
|         1 | 20:12:36.6 |     -1.952 |     -3.387 |
+-----------+------------+------------+------------+
generator list_scan ['f03313c9'] (scan num: 3)


Transient Scan ID: 4     Time: 2025-10-13 20:12:37
Persistent Unique Scan ID: '9294f87e-3ee1-4a91-9e96-ba771af23235'
New stream: 'primary'
+-----------+------------+------------+------------+
|   seq_num |       time |         x1 |         x2 |
+-----------+------------+------------+------------+
|         1 | 20:12:37.0 |     -4.524 |      0.334 |
+-----------+------------+------------+------------+
generator list_scan ['9294f87e'] (scan num: 4)


Transient Scan ID: 5     Time: 2025-10-13 20:12:37
Persistent Unique Scan ID: '7bf8a72b-ec17-49ec-9ca7-ef4a4c731a4b'
New stream: 'primary'
+-----------+------------+------------+------------+
|   seq_num |       time |         x1 |         x2 |
+-----------+------------+------------+------------+
|         1 | 20:12:37.4 |      4.610 |     -0.400 |
+-----------+------------+------------+------------+
generator list_scan ['7bf8a72b'] (scan num: 5)


Transient Scan ID: 6     Time: 2025-10-13 20:12:38
Persistent Unique Scan ID: '26eb6ea6-19e1-478b-8c90-091317200d99'
New stream: 'primary'
+-----------+------------+------------+------------+
|   seq_num |       time |         x1 |         x2 |
+-----------+------------+------------+------------+
|         1 | 20:12:38.7 |      4.818 |      4.194 |
+-----------+------------+------------+------------+
generator list_scan ['26eb6ea6'] (scan num: 6)


Transient Scan ID: 7     Time: 2025-10-13 20:12:39
Persistent Unique Scan ID: 'e0e3cbd8-937e-4079-960d-9b6615ef67c5'
New stream: 'primary'
+-----------+------------+------------+------------+
|   seq_num |       time |         x1 |         x2 |
+-----------+------------+------------+------------+
|         1 | 20:12:39.8 |      1.076 |      3.139 |
+-----------+------------+------------+------------+
generator list_scan ['e0e3cbd8'] (scan num: 7)


Transient Scan ID: 8     Time: 2025-10-13 20:12:40
Persistent Unique Scan ID: 'f1645d60-c252-4a6a-b419-3964522c2869'
New stream: 'primary'
+-----------+------------+------------+------------+
|   seq_num |       time |         x1 |         x2 |
+-----------+------------+------------+------------+
|         1 | 20:12:40.9 |      2.029 |      1.669 |
+-----------+------------+------------+------------+
generator list_scan ['f1645d60'] (scan num: 8)


Transient Scan ID: 9     Time: 2025-10-13 20:12:41
Persistent Unique Scan ID: '379d4f38-e19d-4175-8147-2b6af04ae431'
New stream: 'primary'
+-----------+------------+------------+------------+
|   seq_num |       time |         x1 |         x2 |
+-----------+------------+------------+------------+
|         1 | 20:12:41.9 |      3.128 |     -1.653 |
+-----------+------------+------------+------------+
generator list_scan ['379d4f38'] (scan num: 9)


Transient Scan ID: 10     Time: 2025-10-13 20:12:42
Persistent Unique Scan ID: '3ad0a912-6cee-4159-8578-68d32ab0e282'
New stream: 'primary'
+-----------+------------+------------+------------+
|   seq_num |       time |         x1 |         x2 |
+-----------+------------+------------+------------+
|         1 | 20:12:42.9 |      4.450 |     -3.728 |
+-----------+------------+------------+------------+
generator list_scan ['3ad0a912'] (scan num: 10)


Transient Scan ID: 11     Time: 2025-10-13 20:12:43
Persistent Unique Scan ID: '6f2abcdf-285c-444e-abb9-08c2827d7efe'
New stream: 'primary'
+-----------+------------+------------+------------+
|   seq_num |       time |         x1 |         x2 |
+-----------+------------+------------+------------+
|         1 | 20:12:43.9 |      2.996 |     -0.422 |
+-----------+------------+------------+------------+
generator list_scan ['6f2abcdf'] (scan num: 11)


Transient Scan ID: 12     Time: 2025-10-13 20:12:44
Persistent Unique Scan ID: 'e1c7ac69-6dcf-457b-bf6e-75e5cc775a07'
New stream: 'primary'
+-----------+------------+------------+------------+
|   seq_num |       time |         x1 |         x2 |
+-----------+------------+------------+------------+
|         1 | 20:12:44.9 |      1.706 |     -3.595 |
+-----------+------------+------------+------------+
generator list_scan ['e1c7ac69'] (scan num: 12)


Transient Scan ID: 13     Time: 2025-10-13 20:12:45
Persistent Unique Scan ID: 'aa7056d0-4342-42f8-b42b-926bc4ac2e5c'
New stream: 'primary'
+-----------+------------+------------+------------+
|   seq_num |       time |         x1 |         x2 |
+-----------+------------+------------+------------+
|         1 | 20:12:46.0 |      3.506 |     -1.306 |
+-----------+------------+------------+------------+
generator list_scan ['aa7056d0'] (scan num: 13)


Transient Scan ID: 14     Time: 2025-10-13 20:12:47
Persistent Unique Scan ID: '88bbeec3-b1a0-4f2d-8665-59a1d643183d'
New stream: 'primary'
+-----------+------------+------------+------------+
|   seq_num |       time |         x1 |         x2 |
+-----------+------------+------------+------------+
|         1 | 20:12:47.1 |     -3.903 |     -5.000 |
+-----------+------------+------------+------------+
generator list_scan ['88bbeec3'] (scan num: 14)


Transient Scan ID: 15     Time: 2025-10-13 20:12:48
Persistent Unique Scan ID: '15320f84-9c66-4724-94de-776382172379'
New stream: 'primary'
+-----------+------------+------------+------------+
|   seq_num |       time |         x1 |         x2 |
+-----------+------------+------------+------------+
|         1 | 20:12:48.5 |     -2.540 |      5.000 |
+-----------+------------+------------+------------+
generator list_scan ['15320f84'] (scan num: 15)


Transient Scan ID: 16     Time: 2025-10-13 20:12:49
Persistent Unique Scan ID: '0a3bb280-1f17-4fae-bf56-bcdfa0e64819'
New stream: 'primary'
+-----------+------------+------------+------------+
|   seq_num |       time |         x1 |         x2 |
+-----------+------------+------------+------------+
|         1 | 20:12:49.6 |     -5.000 |      5.000 |
+-----------+------------+------------+------------+
generator list_scan ['0a3bb280'] (scan num: 16)


Transient Scan ID: 17     Time: 2025-10-13 20:12:50
Persistent Unique Scan ID: 'eea5b531-0f5c-40d9-a68a-371a1236d84b'
New stream: 'primary'
+-----------+------------+------------+------------+
|   seq_num |       time |         x1 |         x2 |
+-----------+------------+------------+------------+
|         1 | 20:12:50.8 |     -2.467 |     -0.247 |
+-----------+------------+------------+------------+
generator list_scan ['eea5b531'] (scan num: 17)


Transient Scan ID: 18     Time: 2025-10-13 20:12:52
Persistent Unique Scan ID: '081a19f5-203c-41af-a48c-ec0ae68b3f6f'
New stream: 'primary'
+-----------+------------+------------+------------+
|   seq_num |       time |         x1 |         x2 |
+-----------+------------+------------+------------+
|         1 | 20:12:52.0 |      1.378 |      5.000 |
+-----------+------------+------------+------------+
generator list_scan ['081a19f5'] (scan num: 18)


Transient Scan ID: 19     Time: 2025-10-13 20:12:53
Persistent Unique Scan ID: '3c94d886-229b-43da-a4db-d3301f9b4f79'
New stream: 'primary'
+-----------+------------+------------+------------+
|   seq_num |       time |         x1 |         x2 |
+-----------+------------+------------+------------+
|         1 | 20:12:53.0 |      1.926 |      2.494 |
+-----------+------------+------------+------------+
generator list_scan ['3c94d886'] (scan num: 19)


Transient Scan ID: 20     Time: 2025-10-13 20:12:54
Persistent Unique Scan ID: '45654db0-e7d4-4556-ae38-a79d1b160c1f'
New stream: 'primary'
+-----------+------------+------------+------------+
|   seq_num |       time |         x1 |         x2 |
+-----------+------------+------------+------------+
|         1 | 20:12:54.1 |     -1.658 |      2.264 |
+-----------+------------+------------+------------+
generator list_scan ['45654db0'] (scan num: 20)


Transient Scan ID: 21     Time: 2025-10-13 20:12:55
Persistent Unique Scan ID: '73a9e0bd-ab25-49d2-830e-fa3784057dd2'
New stream: 'primary'
+-----------+------------+------------+------------+
|   seq_num |       time |         x1 |         x2 |
+-----------+------------+------------+------------+
|         1 | 20:12:55.1 |     -3.731 |     -2.393 |
+-----------+------------+------------+------------+
generator list_scan ['73a9e0bd'] (scan num: 21)


Transient Scan ID: 22     Time: 2025-10-13 20:12:56
Persistent Unique Scan ID: '2fdc9682-7a1c-46d2-819c-e40fa7fc8d16'
New stream: 'primary'
+-----------+------------+------------+------------+
|   seq_num |       time |         x1 |         x2 |
+-----------+------------+------------+------------+
|         1 | 20:12:56.3 |     -5.000 |     -2.693 |
+-----------+------------+------------+------------+
generator list_scan ['2fdc9682'] (scan num: 22)


Transient Scan ID: 23     Time: 2025-10-13 20:12:57
Persistent Unique Scan ID: '5dd2a275-8e05-4bfd-a955-8929d0ec4f7a'
New stream: 'primary'
+-----------+------------+------------+------------+
|   seq_num |       time |         x1 |         x2 |
+-----------+------------+------------+------------+
|         1 | 20:12:57.3 |     -2.879 |     -2.095 |
+-----------+------------+------------+------------+
generator list_scan ['5dd2a275'] (scan num: 23)


Transient Scan ID: 24     Time: 2025-10-13 20:12:58
Persistent Unique Scan ID: '4a645ca5-99a4-4109-b3be-57e33bfa8079'
New stream: 'primary'
+-----------+------------+------------+------------+
|   seq_num |       time |         x1 |         x2 |
+-----------+------------+------------+------------+
|         1 | 20:12:58.7 |     -0.430 |      2.140 |
+-----------+------------+------------+------------+
generator list_scan ['4a645ca5'] (scan num: 24)


Transient Scan ID: 25     Time: 2025-10-13 20:12:59
Persistent Unique Scan ID: 'd5580417-60c4-4e5f-9be3-7c0cfe40c759'
New stream: 'primary'
+-----------+------------+------------+------------+
|   seq_num |       time |         x1 |         x2 |
+-----------+------------+------------+------------+
|         1 | 20:12:59.8 |      2.687 |     -1.152 |
+-----------+------------+------------+------------+
generator list_scan ['d5580417'] (scan num: 25)


Transient Scan ID: 26     Time: 2025-10-13 20:13:00
Persistent Unique Scan ID: '2bd76f97-54d6-4880-a6d9-f4a91c9547ff'
New stream: 'primary'
+-----------+------------+------------+------------+
|   seq_num |       time |         x1 |         x2 |
+-----------+------------+------------+------------+
|         1 | 20:13:01.0 |     -3.387 |     -3.086 |
+-----------+------------+------------+------------+
generator list_scan ['2bd76f97'] (scan num: 26)


Transient Scan ID: 27     Time: 2025-10-13 20:13:02
Persistent Unique Scan ID: '4285bc50-b847-4187-9675-42c8f16c2cb2'
New stream: 'primary'
+-----------+------------+------------+------------+
|   seq_num |       time |         x1 |         x2 |
+-----------+------------+------------+------------+
|         1 | 20:13:02.3 |      2.903 |      1.878 |
+-----------+------------+------------+------------+
generator list_scan ['4285bc50'] (scan num: 27)


Transient Scan ID: 28     Time: 2025-10-13 20:13:03
Persistent Unique Scan ID: '6db80041-82ce-46dc-81d3-9b7c3aa46ba3'
New stream: 'primary'
+-----------+------------+------------+------------+
|   seq_num |       time |         x1 |         x2 |
+-----------+------------+------------+------------+
|         1 | 20:13:03.4 |      2.533 |      2.283 |
+-----------+------------+------------+------------+
generator list_scan ['6db80041'] (scan num: 28)


Transient Scan ID: 29     Time: 2025-10-13 20:13:04
Persistent Unique Scan ID: '83d2d093-d639-4d18-8b9e-2a0842fbbce1'
New stream: 'primary'
+-----------+------------+------------+------------+
|   seq_num |       time |         x1 |         x2 |
+-----------+------------+------------+------------+
|         1 | 20:13:04.8 |      3.235 |      1.040 |
+-----------+------------+------------+------------+
generator list_scan ['83d2d093'] (scan num: 29)


Transient Scan ID: 30     Time: 2025-10-13 20:13:06
Persistent Unique Scan ID: 'c4806788-ad63-4896-8614-909acdc51fe7'
New stream: 'primary'
+-----------+------------+------------+------------+
|   seq_num |       time |         x1 |         x2 |
+-----------+------------+------------+------------+
|         1 | 20:13:06.2 |      3.757 |     -1.972 |
+-----------+------------+------------+------------+
generator list_scan ['c4806788'] (scan num: 30)
2025-10-13 20:12:35.566 INFO: Configuring optimization with objective: -himmelblau_2d and outcome constraints: []
2025-10-13 20:12:35.589 INFO: Executing plan <generator object Agent.learn at 0x7f9420728540>
2025-10-13 20:12:35.591 INFO: Change state on <bluesky.run_engine.RunEngine object at 0x7f9422b76f30> from 'idle' -> 'running'
2025-10-13 20:13:06.541 INFO: Change state on <bluesky.run_engine.RunEngine object at 0x7f9422b76f30> from 'running' -> 'idle'
2025-10-13 20:13:06.542 INFO: Cleaned up from plan <generator object Agent.learn at 0x7f9420728540>
('9a845dee-b863-49b0-b632-fe52603de8a3',
 '08435476-b66f-49c7-bead-4a23a3b24ab4',
 'f03313c9-7c35-497d-84e3-838cb744b10c',
 '9294f87e-3ee1-4a91-9e96-ba771af23235',
 '7bf8a72b-ec17-49ec-9ca7-ef4a4c731a4b',
 '26eb6ea6-19e1-478b-8c90-091317200d99',
 'e0e3cbd8-937e-4079-960d-9b6615ef67c5',
 'f1645d60-c252-4a6a-b419-3964522c2869',
 '379d4f38-e19d-4175-8147-2b6af04ae431',
 '3ad0a912-6cee-4159-8578-68d32ab0e282',
 '6f2abcdf-285c-444e-abb9-08c2827d7efe',
 'e1c7ac69-6dcf-457b-bf6e-75e5cc775a07',
 'aa7056d0-4342-42f8-b42b-926bc4ac2e5c',
 '88bbeec3-b1a0-4f2d-8665-59a1d643183d',
 '15320f84-9c66-4724-94de-776382172379',
 '0a3bb280-1f17-4fae-bf56-bcdfa0e64819',
 'eea5b531-0f5c-40d9-a68a-371a1236d84b',
 '081a19f5-203c-41af-a48c-ec0ae68b3f6f',
 '3c94d886-229b-43da-a4db-d3301f9b4f79',
 '45654db0-e7d4-4556-ae38-a79d1b160c1f',
 '73a9e0bd-ab25-49d2-830e-fa3784057dd2',
 '2fdc9682-7a1c-46d2-819c-e40fa7fc8d16',
 '5dd2a275-8e05-4bfd-a955-8929d0ec4f7a',
 '4a645ca5-99a4-4109-b3be-57e33bfa8079',
 'd5580417-60c4-4e5f-9be3-7c0cfe40c759',
 '2bd76f97-54d6-4880-a6d9-f4a91c9547ff',
 '4285bc50-b847-4187-9675-42c8f16c2cb2',
 '6db80041-82ce-46dc-81d3-9b7c3aa46ba3',
 '83d2d093-d639-4d18-8b9e-2a0842fbbce1',
 'c4806788-ad63-4896-8614-909acdc51fe7')

Now we can view the results.

agent.plot_objective("x1", "x2", "himmelblau_2d")
agent.summarize()
x1, x2 vs. himmelblau_2d

The contour plot visualizes the predicted outcomes for himmelblau_2d across a two-dimensional parameter space, with other parameters held fixed at their status_quo value (or mean value if status_quo is unavailable). This plot helps in identifying regions of optimal performance and understanding how changes in the selected parameters influence the predicted outcomes. Contour lines represent levels of constant predicted values, providing insights into the gradient and potential optima within the parameter space.

trial_index arm_name trial_status generation_node himmelblau_2d x1 x2
0 0 0_0 COMPLETED CenterOfSearchSpace 170.000000 0.000000 0.000000
1 1 1_0 COMPLETED Sobol 46.514064 2.040982 3.283578
2 2 2_0 COMPLETED Sobol 118.264722 -1.951735 -3.387466
3 3 3_0 COMPLETED Sobol 226.266029 -4.523858 0.333851
4 4 4_0 COMPLETED Sobol 102.108437 4.610390 -0.399889
5 5 5_0 COMPLETED MBM 506.680108 4.818014 4.194297
6 6 6_0 COMPLETED MBM 60.393143 1.075713 3.139399
7 7 7_0 COMPLETED MBM 31.971291 2.028895 1.668889
8 8 8_0 COMPLETED MBM 9.533461 3.127848 -1.652980
9 9 9_0 COMPLETED MBM 154.582178 4.450215 -3.728192
10 10 10_0 COMPLETED MBM 20.631330 2.995699 -0.422169
11 11 11_0 COMPLETED MBM 194.686921 1.706050 -3.594578
12 12 12_0 COMPLETED MBM 3.193986 3.506171 -1.306409
13 13 13_0 COMPLETED MBM 199.332323 -3.902521 -5.000000
14 14 14_0 COMPLETED MBM 239.209374 -2.540234 5.000000
15 15 15_0 COMPLETED MBM 530.000000 -5.000000 5.000000
16 16 16_0 COMPLETED MBM 115.105665 -2.467074 -0.247374
17 17 17_0 COMPLETED MBM 392.327035 1.378062 5.000000
18 18 18_0 COMPLETED MBM 24.312468 1.926225 2.494115
19 19 19_0 COMPLETED MBM 48.331642 -1.658060 2.263731
20 20 20_0 COMPLETED MBM 25.321712 -3.731325 -2.393158
21 21 21_0 COMPLETED MBM 150.365542 -5.000000 -2.693323
22 22 22_0 COMPLETED MBM 53.248075 -2.878897 -2.094929
23 23 23_0 COMPLETED MBM 83.368551 -0.429501 2.140436
24 24 24_0 COMPLETED MBM 33.214580 2.687453 -1.151802
25 25 25_0 COMPLETED MBM 7.575858 -3.387188 -3.085602
26 26 26_0 COMPLETED MBM 0.808296 2.903160 1.877598
27 27 27_0 COMPLETED MBM 5.860068 2.532532 2.283232
28 28 28_0 COMPLETED MBM 7.452617 3.234987 1.040321
29 29 29_0 COMPLETED MBM 1.724440 3.757113 -1.971554